package de.fanuffel.generic;

import de.fanuffel.generic.functions.Function;
import de.fanuffel.generic.structures.Pair;

/*
 * $Id: Equal.java,v 1.1 2007-01-29 17:00:56 klose Exp $
 */

public interface Equal<A, B> {
  public A as(B b);

  public B to(A a);
};

class Transitivity<A, B, C> implements
    Function<Pair<Equal<A, B>, Equal<B, C>>, Equal<A, C>> {
  public Equal<A, C> eval(final Pair<Equal<A, B>, Equal<B, C>> premises) {
    return new Equal<A, C>() {
      public A as(C c) {
        return premises.getFirst().as(premises.getSecond().as(c));
      }

      public C to(A a) {
        return premises.getSecond().to(premises.getFirst().to(a));
      }
    };
  }
}

class Reflexivity<A> implements Equal<A, A> {
  public A as(A x) {
    return x;
  }

  public A to(A x) {
    return x;
  }

};

class FuncEqual<A, B, C> implements
    Function<Equal<A, B>, Equal<Function<A, C>, Function<B, C>>> {

  public Equal<Function<A, C>, Function<B, C>> eval(final Equal<A, B> equal) {
    return new Equal<Function<A, C>, Function<B, C>>() {
      public Function<A, C> as(final Function<B, C> f) {
        return new Function<A, C>() {
          public C eval(final A a) {
            return f.eval(equal.to(a));
          }
        };
      }

      public Function<B, C> to(final Function<A, C> f) {
        return new Function<B, C>() {
          public C eval(final B b) {
            return f.eval(equal.as(b));
          }
        };
      }
    };
  }

}

class Curry<A, B, C> implements
    Equal<Function<Pair<A, B>, C>, Function<A, Function<B, C>>> {
  public Function<Pair<A, B>, C> as(final Function<A, Function<B, C>> b) {
    return new Function<Pair<A, B>, C>() {
      public C eval(Pair<A, B> argument) {
        return b.eval(argument.getFirst()).eval(argument.getSecond());
      }
    };
  }

  public Function<A, Function<B, C>> to(final Function<Pair<A, B>, C> f) {
    return new Function<A, Function<B, C>>() {
      public Function<B, C> eval(final A a) {
        return new Function<B, C>() {
          public C eval(B b) {
            return f.eval(Pair.make(a, b));
          }
        };
      }
    };
  }
}